package com.zzl.study.nacos.registry;

import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingMaintainService;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.zzl.study.nacos.NacosServiceMultiManager;
import com.zzl.study.nacos.properties.NacosMultiDiscoveryProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;

import java.util.ArrayList;
import java.util.List;

import static org.springframework.util.ReflectionUtils.rethrowRuntimeException;

/**
 * Nacos服务多注册中心注册器
 *
 * @author: zhangzl
 * @date: 2025/7/14 22:24
 * @version: 1.0
 */
@Slf4j
public class NacosServiceMultiRegistry implements ServiceRegistry<Registration> {

    /**
     * 服务状态：UP
     */
    private static final String STATUS_UP = "UP";

    /**
     * 服务状态：DOWN
     */
    private static final String STATUS_DOWN = "DOWN";

    /**
     * Nacos多注册中心配置
     */
    private final NacosMultiDiscoveryProperties nacosMultiDiscoveryProperties;

    /**
     * Nacos多注册中心管理器
     */
    private final NacosServiceMultiManager nacosServiceMultiManager;

    /**
     * 构造函数
     *
     * @param nacosServiceMultiManager      Nacos多注册中心管理器
     * @param nacosMultiDiscoveryProperties Nacos多注册中心配置
     */
    public NacosServiceMultiRegistry(NacosServiceMultiManager nacosServiceMultiManager, NacosMultiDiscoveryProperties nacosMultiDiscoveryProperties) {
        this.nacosMultiDiscoveryProperties = nacosMultiDiscoveryProperties;
        this.nacosServiceMultiManager = nacosServiceMultiManager;
    }

    @Override
    public void register(Registration registration) {
        if (StringUtils.isEmpty(registration.getServiceId())) {
            log.warn("No service to register for nacos client...");
            return;
        }
        List<NamingService> namingServiceList = namingServices();
        String serviceId = registration.getServiceId();
        String group = this.nacosMultiDiscoveryProperties.getGroup();
        Instance instance = getNacosInstanceFromRegistration(registration);
        try {
            for (NamingService namingService : namingServiceList) {
                namingService.registerInstance(serviceId, group, instance);
            }
            log.info("nacos registry, {} {} {}:{} register finished", group, serviceId, instance.getIp(), instance.getPort());
        } catch (Exception e) {
            if (this.nacosMultiDiscoveryProperties.isFailFast()) {
                log.error("nacos registry, {} register failed...{},", serviceId, registration.toString(), e);
                rethrowRuntimeException(e);
            } else {
                log.warn("Failfast is false. {} register failed...{},", serviceId, registration.toString(), e);
            }
        }
    }

    @Override
    public void deregister(Registration registration) {
        log.info("De-registering from Nacos Server now...");
        if (StringUtils.isEmpty(registration.getServiceId())) {
            log.warn("No dom to de-register for nacos client...");
            return;
        }
        List<NamingService> namingServiceList = namingServices();
        String serviceId = registration.getServiceId();
        String group = this.nacosMultiDiscoveryProperties.getGroup();
        try {
            for (NamingService namingService : namingServiceList) {
                namingService.deregisterInstance(serviceId, group, registration.getHost(), registration.getPort(), this.nacosMultiDiscoveryProperties.getClusterName());
            }
        } catch (Exception e) {
            log.error("ERR_NACOS_DEREGISTER, de-register failed...{},", registration.toString(), e);
        }
        log.info("De-registration finished.");
    }

    @Override
    public void close() {
        try {
            this.nacosServiceMultiManager.nacosServiceShutDownAll();
        } catch (NacosException e) {
            log.error("Nacos namingService shutDown failed", e);
        }
    }

    @Override
    public void setStatus(Registration registration, String status) {
        if (!STATUS_UP.equalsIgnoreCase(status) && !STATUS_DOWN.equalsIgnoreCase(status)) {
            log.warn("can't support status {},please choose UP or DOWN", status);
            return;
        }
        String serviceId = registration.getServiceId();
        Instance instance = getNacosInstanceFromRegistration(registration);
        instance.setEnabled(!STATUS_DOWN.equalsIgnoreCase(status));
        try {
            List<NamingMaintainService> namingMaintainServiceList = namingMaintainServices();
            for (NamingMaintainService namingMaintainService : namingMaintainServiceList) {
                namingMaintainService.updateInstance(serviceId, this.nacosMultiDiscoveryProperties.getGroup(), instance);
            }
        } catch (Exception e) {
            throw new RuntimeException("update nacos instance status fail", e);
        }
    }

    @Override
    public Object getStatus(Registration registration) {
        String serviceName = registration.getServiceId();
        String group = this.nacosMultiDiscoveryProperties.getGroup();
        try {
            List<NamingService> namingServiceList = namingServices();
            List<Instance> instanceList = new ArrayList<>();
            for (NamingService namingService : namingServiceList) {
                List<Instance> instances = namingService.getAllInstances(serviceName, group);
                for (Instance instance : instances) {
                    if (instance.getIp().equalsIgnoreCase(this.nacosMultiDiscoveryProperties.getIp()) && instance.getPort() == this.nacosMultiDiscoveryProperties.getPort()) {
                        instanceList.add(instance);
                        break;
                    }
                }
            }
            // 任意集群当中的服务实例可用则认为是UP状态
            boolean enabled = instanceList.stream().anyMatch(Instance::isEnabled);
            return enabled ? STATUS_UP : STATUS_DOWN;
        } catch (Exception e) {
            log.error("get all instance of {} error,", serviceName, e);
        }
        return null;
    }

    /**
     * 获取Nacos实例
     *
     * @param registration 注册信息
     * @return Nacos实例
     */
    private Instance getNacosInstanceFromRegistration(Registration registration) {
        Instance instance = new Instance();
        instance.setIp(registration.getHost());
        instance.setPort(registration.getPort());
        instance.setWeight(this.nacosMultiDiscoveryProperties.getWeight());
        instance.setClusterName(this.nacosMultiDiscoveryProperties.getClusterName());
        instance.setEnabled(this.nacosMultiDiscoveryProperties.isInstanceEnabled());
        instance.setMetadata(registration.getMetadata());
        instance.setEphemeral(this.nacosMultiDiscoveryProperties.isEphemeral());
        return instance;
    }

    /**
     * 获取所有Nacos注册中心服务
     *
     * @return 所有Nacos注册中心服务
     */
    private List<NamingService> namingServices() {
        return this.nacosServiceMultiManager.getNamingServices();
    }

    /**
     * 获取所有Nacos服务维护服务
     *
     * @return 所有Nacos服务维护服务
     */
    private List<NamingMaintainService> namingMaintainServices() {
        return this.nacosServiceMultiManager.getNamingMaintainServices();
    }

}
